/*
 * Copyright (c) 2021 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>

#include "iniparser.h"
#include "hi_ext_util.h"
#include "hi_app_base.h"

#define CFG_KEY_MAX         256 // config key buf的最大长度
#define WAIT_QUIT_TMOUT     9000 // reboot等待时间

static dictionary *g_appCfg = NULL; // 全局配置
static pthread_t g_mainThrdId = 0; // 主线程ID，调用app_base_init()的线程
static EvtMon *g_mainEvtMon = NULL; // 主线程关联的EvtMon
static bool g_rebootReq = false; // 是否被请求reboot
static pthread_t g_wdThrdId = 0; // watchdog thread id

/**
    通过启动线程来实现watchdog timer.
*/
static void* WatchdogThrd(void* param)
{
    long timeout = (long)(uintptr_t)param;
    HI_ASSERT(timeout > 0);
    usleep(timeout * HI_USLEEP_MS);

    if (g_rebootReq) {
        LOGE("@@@  app quit watchdog timeout, reboot system\n");
        kill(1, SIGTERM);
    } else {
        LOGE("@@@ app quit watchdog timeout, exit proc\n");
        _exit(HI_EC_WD_TIMEOUT);
    }
    return NULL;
}

/**
    app base初始化.
*/
int AppBaseInit(const char* cfgFilePath)
{
    // 不允许重复init
    HI_ASSERT(!g_mainThrdId);
    HI_ASSERT(!g_mainEvtMon);

    g_mainThrdId = pthread_self();
    LOGI("mainThrdId=%ld\n", (long)g_mainThrdId);

    if (cfgFilePath && *cfgFilePath) {
        g_appCfg = iniparser_load(cfgFilePath);
        if (!g_appCfg) {
            LOGE("load '%s' FAIL, all config to default\n", cfgFilePath);
        }
    } else {
        LOGW("no config file provided, all config to default\n");
        g_appCfg = NULL;
    }

    if (EmCreate(&g_mainEvtMon) < 0) {
        HI_ASSERT(0);
    }
    return 0;
}

/**
    app base去初始化.
*/
void AppBaseExit(void)
{
    // 不允许重复exit
    HI_ASSERT(g_mainEvtMon);
    bool ret = IsMainThrd();
    HI_ASSERT(ret);

    // 检测mainEvtMon是否已经退出循环
    EmDestroy(g_mainEvtMon);
    g_mainEvtMon = NULL;
    g_mainThrdId = 0;

    if (g_appCfg) {
        iniparser_freedict(g_appCfg);
        g_appCfg = NULL;
    }

    // reboot处理
    if (g_rebootReq) {
        LOGI("@@@ kill(1, SIGINT) to reboot\n");
        kill(1, SIGINT);
    }
    g_wdThrdId = 0;
}

/**
    启动watchdog.
*/
void StartAppWd(long watchdogTimeout)
{
    if (g_wdThrdId) {
        LOGW("start watchdog ignore, for it started\n");
        return;
    }

    watchdogTimeout = watchdogTimeout < 0 ? WAIT_QUIT_TMOUT : watchdogTimeout;
    if (watchdogTimeout > 0) {
        LOGI("@@@ start watchdog, timeout=%ldms\n", watchdogTimeout);
        DetachThreadCreate(&g_wdThrdId, WatchdogThrd, (void*)(uintptr_t)watchdogTimeout);
    } else {
        LOGW("start watchdog ignore, for timeout=0\n");
    }
}

/**
    请求reboot系统.
    对于reboot，为确保能退成进程并reboot，在此函数中直接start watchdog.
*/
void RebootSystem(long watchdogTimeout)
{
    // 需要先启动watchdog，再quit main emon
    LOGI("@@@ reboot req, quit main thrd loop\n");
    g_rebootReq = true;
    StartAppWd(watchdogTimeout);
    EmQuit(MainEvtMon());
}

/**
    返回主线程ID.
*/
pthread_t MainThrdId(void)
{
    HI_ASSERT(g_mainThrdId);
    return g_mainThrdId;
}

/**
    返回主线程关联的EvtMon.
*/
EvtMon* MainEvtMon(void)
{
    HI_ASSERT(g_mainEvtMon);
    return g_mainEvtMon;
}

/**
    判断当前线程是否是主线程.
*/
bool IsMainThrd(void)
{
    return pthread_self() == g_mainThrdId;
}

/**
    获取key对应的int型配置项.
*/
int GetCfgInt(const char* key, int defVal)
{
    HI_ASSERT(key && *key);
    return g_appCfg ? iniparser_getint(g_appCfg, key, defVal) : defVal;
}

/**
    获取key对应的double型配置项.
*/
double GetCfgDouble(const char* key, double defVal)
{
    HI_ASSERT(key && *key);
    return g_appCfg ? iniparser_getdouble(g_appCfg, key, defVal) : defVal;
}

/**
    获取key对应的bool型配置项.
*/
bool GetCfgBool(const char* key, bool defVal)
{
    static const size_t trueSize = 4;
    static const size_t falseSize = 5;
    const char *val;

    val = GetCfgStr(key, NULL);
    if (val && *val) {
        if (val[0] == '1' || val[0] == 'y' || val[0] == 'Y') {
            return true;
        } else if (val[0] == '0' || val[0] == 'n' || val[0] == 'N') {
            return false;
        } else if (HiStrincmp(val, "true", trueSize) == 0) {
            return true;
        } else if (HiStrincmp(val, "false", falseSize) == 0) {
            return false;
        } else {
            return defVal;
        }
    } else {
        return defVal;
    }
}

/**
    获取key对应的string型配置项.
*/
const char* GetCfgStr(const char* key, const char* defVal)
{
    HI_ASSERT(key && *key);
    return g_appCfg ? iniparser_getstring(g_appCfg, key, defVal) : defVal;
}

/**
    获取section+field对应的int型配置项.
*/
int SectGetCfgInt(const char* section, const char* field, int defVal)
{
    HI_ASSERT(field && *field);
    char key[CFG_KEY_MAX];

    if (snprintf_s(key, sizeof(key), sizeof(key) - 1, "%s:%s", (section ? section : ""), field) < 0) {
        HI_ASSERT(0);
    }
    return GetCfgInt(key, defVal);
}

/**
    获取section+field对应的double型配置项.
*/
double SectGetCfgDouble(const char* section, const char* field, double defVal)
{
    HI_ASSERT(field && *field);
    char key[CFG_KEY_MAX];

    if (snprintf_s(key, sizeof(key), sizeof(key) - 1, "%s:%s", (section ? section : ""), field) < 0) {
        HI_ASSERT(0);
    }
    return GetCfgDouble(key, defVal);
}

/**
    获取section+field对应的bool型配置项.
*/
bool SectGetCfgBool(const char* section, const char* field, bool defVal)
{
    HI_ASSERT(field && *field);
    char key[CFG_KEY_MAX];

    if (snprintf_s(key, sizeof(key), sizeof(key) - 1, "%s:%s", (section ? section : ""), field) < 0) {
        HI_ASSERT(0);
    }
    return GetCfgBool(key, defVal);
}

/**
    获取section+field对应的string型配置项.
*/
const char* SectGetCfgStr(const char* section, const char* field, const char* defVal)
{
    HI_ASSERT(field && *field);
    char key[CFG_KEY_MAX];

    if (snprintf_s(key, sizeof(key), sizeof(key) - 1, "%s:%s", (section ? section : ""), field) < 0) {
        HI_ASSERT(0);
    }
    return GetCfgStr(key, defVal);
}

